#ifndef _DX8_GRAPHICS_H_
#define _DX8_GRAPHICS_H_

#include "graphics/graphics.h"
#include <D3D8.h>
#include <string>
#include <vector>

class DX8VertexBuffer;
class DX8IndexBuffer;
class DX8IBSet;
class DX8VBSet;
class Dx8Graphics : public Graphics
{
public:
	enum				VERTEX_PROCESS_MODE
	{
		VP_HW_ONLY,
		VP_SOFTWARE_ONLY,
		VP_MIXED
	};
	enum				BUG_ENTRY
	{
		BUG_SQUARE_TEXTURES,
		BUG_16BIT_ONLY,
		BUG_VISTA_ALTTAB,
		BUG_SOFTWARE_PROCESSING,
		BUG_COUNT
	};


	// instantiate the graphics driver
	static void		Create();
	static void		Delete();

					Dx8Graphics();
	virtual			~Dx8Graphics();

	// startup and maintainance
	virtual	bool	Init();
	virtual bool	InitWindow(unsigned int Width, unsigned int Height, bool Fullscreen, bool EnableDebug = false);
	virtual bool	ResetWindow(unsigned int Width, unsigned int Height, bool Fullscreen);
	virtual void	RegisterFormats();
	virtual void	GetBestWideScreenRes(unsigned int& Width, unsigned int& Height, bool bGetForBackbuffer = true);
	virtual void	TakeScreenshot(const std::string & Name){};

	// flushing and recreating assets
	virtual void	Flush();
	virtual void	Recreate();

	virtual	bool	Shutdown();
	virtual	bool	ShutdownWindow();
	virtual	bool	Resize(unsigned int Width, unsigned int Height);
	
	// begin and end scene
	virtual bool	BeginScene();
	virtual void	EndScene();
	virtual bool	Present();
	virtual void	Clear(COLOR c, float Depth = 1.0f, int Stencil = 0, bool ClearRGB = true, bool ClearDepth= true, bool ClearStencil= true);

	// texture stage stuff
	virtual void	SetTexStage(unsigned int TexStage, STAGE_TYPE type, BLEND_OP Op);
	virtual void	SetRenderState(RENDER_STATE r, unsigned int val) ;
	virtual void	SetTexture(unsigned int Stage, Image* pImg) ;
	// initialize internals for usual 2D and 3D options
	virtual void	Start2D();
	virtual void	Start3D();
	// 2D stuff
	virtual void	DrawVertex2D(Vertex2D*	VertA, unsigned int VertC, PRIMITIVE_TYPE Type = PT_TRI_LIST);
	virtual void	DrawIndexVertex2D(Vertex2D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type = PT_TRI_LIST);
	virtual void	DrawFatIndexVertex2D(FatVertex2D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type = PT_TRI_LIST);
	virtual void	DrawVertex3D(Vertex3D*	VertA, unsigned int VertC, PRIMITIVE_TYPE Type = PT_TRI_LIST);
	virtual void	DrawIndexVertex3D(Vertex3D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type = PT_TRI_LIST);
	// transforms
	virtual void	SetTransform(TRANSFORM_STAGE ts, const math::Mat4x4& mat);
	virtual void	SetViewport(const math::Viewport& vp);
	// image manipulation
	virtual Image*		CreateComposite(int	width, int height, int depth, const std::vector<ImageLayer>& Layers);


	LPDIRECT3DDEVICE8   GetDevice() const { return m_pd3dDevice;	};
	virtual void*		GetDeviceDriver() const { return m_pd3dDevice;	};
	virtual void*		GetFlashDriver();

	virtual Image*	LoadImageFromFile(const std::string& Name);
	virtual Image*	LoadImageFromMemory(unsigned char* pData, unsigned int	Size);
	virtual Image*	CreateDynamicImage(unsigned short Width = 256, unsigned short Height= 256, unsigned short Depth=32);
	virtual Font*	CreateTTFFont(const std::string& Name);
	virtual AVI*	LoadMovie(const std::string& Name);
	virtual void	DestroyMovie(AVI* pAVI);


	virtual void		SetFilter2D(bool b);

	// helpers
    static std::string		GetDXError(HRESULT _Res);
	VERTEX_PROCESS_MODE		GetProcessMode() const { return m_ProcessMode; };

	const std::string&		GetGraphicsInfo() { return m_GraphicsInfo; };
	const std::string&		GetSysInfo() { return m_SystemInfo; };



	//void				Blit(Image* pImage);
	bool				HasBug(BUG_ENTRY Entry) { return m_BugList[Entry]; };
	void				SetupWindow(unsigned int Width, unsigned int Height, bool Fullscreen, bool Init = false);
	virtual ImageCache*		CreateCache(int Width, int Height, int Depth);
	bool				HasInvalidState() const { return m_InvalidState; };
	
	HWND			 GetHandle() const { return m_WindowHandle; };
	void			 SetHWND(HWND Handle) { m_WindowHandle = Handle; };

	DX8VertexBuffer*	GetTempVB(int VertC);
	DX8IndexBuffer*		GetTempIB(int IndexC);
private:
	/// initialize the caps
    void                InitCaps(); 
	/// map from internal format -> DX format
	bool				MapPrimitiveToDX(PRIMITIVE_TYPE Type, unsigned int VertC, D3DPRIMITIVETYPE& Pt, unsigned int& PrimitiveC);
	
	void				EnumerateDisplayModes(D3DFORMAT Format, unsigned int MinWidth, unsigned int MinHeight, unsigned int MinRefresh);
	void				BuildPresentationParams(unsigned int Width, unsigned int Height, bool Fullscreen);
	//bool				VerifyBackbufferDims(unsigned int Width, unsigned int Height); ADRIAN: doesn't work, but i'll just leave it commented out for the record

	void				SetupViewport(int Width, int Height);

	void				FlushScreenshot();
	void				FlushBuffers();
	/// DX stuff
	LPDIRECT3D8		    m_pD3D;			/// Direct 3D context
	LPDIRECT3DDEVICE8   m_pd3dDevice;	/// Direct 3D device
    D3DDISPLAYMODE		m_d3ddm;		/// Display Mode params
	D3DCAPS8			m_d3d8Caps;         /// Caps
	D3DPRESENT_PARAMETERS m_d3dpp; 
	VERTEX_PROCESS_MODE	m_ProcessMode;

	bool				m_HasZBuffer;
	bool				m_HasStencilBuffer;
	bool				m_InvalidState;

	std::vector<D3DDISPLAYMODE>	m_Modes;

	std::string			m_GraphicsInfo, m_SystemInfo;
	bool				m_BugList[BUG_COUNT];
	unsigned int		m_DeviceID;

	std::vector<DX8VBSet*>	m_VBSets;
	std::vector<DX8IBSet*>	m_IBSets;

	//SOME OPTIMIZATIONS
	//current texture
	Image*				m_pCurTexture[2];

	unsigned int		m_CurRenderStates[Graphics::RENDER_STATE_C]; 

	BLEND_OP			m_CurTextureStates[2][Graphics::STAGE_TYPE_COUNT]; 
	HWND				m_WindowHandle;



};


Dx8Graphics*	GetDXGraphics();

#endif // _DX8_GRAPHICS_H_